<?php

// +----------------------------------------------------------------------
// | OneThink [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2013 http://www.onethink.cn All rights reserved.
// +----------------------------------------------------------------------
// | Author: 麦当苗儿 <zuojiazi@vip.qq.com> <http://www.zjzit.cn>
// +----------------------------------------------------------------------

/**
 * 把返回的数据集转换成Tree
 * @param array $list 要转换的数据集
 * @param string $pid parent标记字段
 * @param string $level level标记字段
 * @return array
 * @author 麦当苗儿 <zuojiazi@vip.qq.com>
 */
function list_to_tree($list, $pk = 'id', $pid = 'pid', $child = '_child', $root = 0) {
// 创建Tree
    $tree = array();
    if (is_array($list)) {
// 创建基于主键的数组引用
        $refer = array();
        foreach ($list as $key => $data) {
            $refer[$data[$pk]] = & $list[$key];
        }
        foreach ($list as $key => $data) {
// 判断是否存在parent
            $parentId = $data[$pid];
            if ($root == $parentId) {
                $tree[] = & $list[$key];
            } else {
                if (isset($refer[$parentId])) {
                    $parent = & $refer[$parentId];
                    $parent[$child][] = & $list[$key];
                }
            }
        }
    }
    return $tree;
}

/**
 * 将list_to_tree的树还原成列表
 * @param  array $tree  原来的树
 * @param  string $child 孩子节点的键
 * @param  string $order 排序显示的键，一般是主键 升序排列
 * @param  array  $list  过渡用的中间数组，
 * @return array        返回排过序的列表数组
 * @author yangweijie <yangweijiester@gmail.com>
 */
function tree_to_list($tree, $child = '_child', $order = 'id', &$list = array()) {
    if (is_array($tree)) {
        $refer = array();
        foreach ($tree as $key => $value) {
            $reffer = $value;
            if (isset($reffer[$child])) {
                unset($reffer[$child]);
                tree_to_list($value[$child], $child, $order, $list);
            }
            $list[] = $reffer;
        }
        $list = list_sort_by($list, $order, $sortby = 'asc');
    }
    return $list;
}

/**
 * 格式化字节大小
 * @param  number $size      字节数
 * @param  string $delimiter 数字和单位分隔符
 * @return string            格式化后的带单位的大小
 * @author 麦当苗儿 <zuojiazi@vip.qq.com>
 */
function format_bytes($size, $delimiter = '') {
    $units = array('B', 'KB', 'MB', 'GB', 'TB', 'PB');
    for ($i = 0; $size >= 1024 && $i < 5; $i++)
        $size /= 1024;
    return round($size, 2) . $delimiter . $units[$i];
}

/**
 * 设置跳转页面URL
 * 使用函数再次封装，方便以后选择不同的存储方式（目前使用cookie存储）
 * @author 麦当苗儿 <zuojiazi@vip.qq.com>
 */
function set_redirect_url($url) {
    cookie('redirect_url', $url);
}

/**
 * 获取跳转页面URL
 * @return string 跳转页URL
 * @author 麦当苗儿 <zuojiazi@vip.qq.com>
 */
function get_redirect_url() {
    $url = cookie('redirect_url');
    return empty($url) ? __APP__ : $url;
}

/**
 * 时间戳格式化
 * @param int $time
 * @return string 完整的时间显示
 * @author huajie <banhuajie@163.com>
 * @since 1.1 <2015-8-6> SoChishun 新增字符串时间转换功能
 */
function time_format($time = NULL, $format = 'Y-m-d H:i') {
    if (!is_numeric($time)) {
        $time = strtotime($time);
    }
    $time = $time === NULL ? NOW_TIME : intval($time);
    return date($format, $time);
}

//基于数组创建目录和文件
function create_dir_or_files($files) {
    foreach ($files as $key => $value) {
        if (substr($value, -1) == '/') {
            mkdir($value);
        } else {
            @file_put_contents($value, '');
        }
    }
}

if (!function_exists('array_column')) {

    function array_column(array $input, $columnKey, $indexKey = null) {
        $result = array();
        if (null === $indexKey) {
            if (null === $columnKey) {
                $result = array_values($input);
            } else {
                foreach ($input as $row) {
                    $result[] = $row[$columnKey];
                }
            }
        } else {
            if (null === $columnKey) {
                foreach ($input as $row) {
                    $result[$row[$indexKey]] = $row;
                }
            } else {
                foreach ($input as $row) {
                    $result[$row[$indexKey]] = $row[$columnKey];
                }
            }
        }
        return $result;
    }

}

// =========================================================================================
// @author   sutroon <14507247@qq.com>
// @since 1.0 2014-6-24 by sutroon; 2.0 2014-12-22 by sutroon 重构函数命名,统一加前缀sofn_
// =========================================================================================
/**
 * 发送sokcet数据
 * @param string $in
 * @param string $ip
 * @param int $port
 * @return string
 * @since 1.0 2014-08-14 by kerry
 * @example socket_send($tsid . ',' . $state)
 */
function sofn_socket_send($in, $ip = '', $port = 8855) {
    if (!$ip) {
        $ip = $_SERVER['SERVER_NAME'];
    }
    error_reporting(E_ALL);
    set_time_limit(0);
    $fp = stream_socket_client("udp://$ip:$port", $errno, $errstr);
// echo 'stream_socket_client("udp://'.$ip.':'.$port.'", $errno, $errstr)';
    if (!$fp) {
        return "ERROR: $errno - $errstr";
    } else {
        fwrite($fp, $in);
        return fread($fp, 26);
        fclose($fp);
    }
}

/**
 * 合并数组为字符串
 * 
 * @param string $glue 合并符号
 * @param array $arr 数组
 * @param int $start 开始索引
 * @param int $end 结束索引
 * @return string
 * @since 1.0 2015-2-7 by sutroon
 * @example sofn_array_implode('|', $item, 4)
 */
function sofn_array_implode($glue, $arr, $start = 0, $end = 0) {
    if ($end > 0) {
        $len = count($arr);
        if ($end >= $len) {
            $end = $len - 1;
        }
    }
    if ($start > 0 && $end > 0 && $start > $end) {
        $start = $end;
    }
    $i = 0;
    $str = '';
    foreach ($arr as $key => $value) {
        if ($start > 0 && $i < $start) {
            $i++;
            continue;
        }
        if ($end > 0 && $i > $end) {
            break;
        }
        $str .= $glue . $value;
        $i++;
    }
    return $str ? substr($str, 1) : $str;
}

/**
 * 字符串截取，支持中文和其他编码
 * @param string $str 需要转换的字符串
 * @param string $start 开始位置
 * @param string $length 截取长度 默认null
 * @param string $suffix 截断显示字符 默认false
 * @param string $charset 编码格式 默认utf-8
 * @return string
 * @since 1.0 <2015-4-3> SoChishun Refactoring by \Org\Util\String::msubstr
 * @since 1.1 <2015-4-21> SoChishun 新增缩略号自动长度判断逻辑
 */
function sofn_msubstr($str, $start = 0, $length = NULL, $suffix = false, $charset = "utf-8") {
    if (function_exists("mb_substr"))
        $slice = mb_substr($str, $start, is_null($length) ? mb_strlen($str, $charset) - $start : $length, $charset);
    elseif (function_exists('iconv_substr')) {
        $slice = iconv_substr($str, $start, is_null($length) ? iconv_strlen($str, $charset) - $start : $length, $charset);
    } else {
        $re['utf-8'] = "/[\x01-\x7f]|[\xc2-\xdf][\x80-\xbf]|[\xe0-\xef][\x80-\xbf]{2}|[\xf0-\xff][\x80-\xbf]{3}/";
        $re['gb2312'] = "/[\x01-\x7f]|[\xb0-\xf7][\xa0-\xfe]/";
        $re['gbk'] = "/[\x01-\x7f]|[\x81-\xfe][\x40-\xfe]/";
        $re['big5'] = "/[\x01-\x7f]|[\x81-\xfe]([\x40-\x7e]|\xa1-\xfe])/";
        preg_match_all($re[$charset], $str, $match);
        $slice = join("", array_slice($match[0], $start, $length));
    }
    if ($suffix && strlen($slice) < strlen($str)) {
        $slice.=true === $suffix ? '...' : $suffix;
    }
    return $slice;
}

/**
 * 绝对路径转相对路径
 * @param string $path
 * @return string
 * @since 1.0 <2015-10-9> SoChishun Added.
 */
function sofn_path_ator($path) {
    $root = $_SERVER['DOCUMENT_ROOT'];
    $path = substr($path, strlen($root));
    if ('/' != DIRECTORY_SEPARATOR) {
        $path = str_replace(DIRECTORY_SEPARATOR, '/', $path);
    }
    return $path;
}

/**
 * 相对路径转绝对路径
 * @param string $path
 * @return string
 * @since 1.0 <2015-10-9> SoChishun Added.
 */
function sofn_path_rtoa($path) {
    $root = $_SERVER['DOCUMENT_ROOT'];
    if ('/' != DIRECTORY_SEPARATOR) {
        $path = str_replace('/', DIRECTORY_SEPARATOR, $path);
    }
    return $root . $path;
}

/**
 * 重命名路径中的文件名
 * <br />用户生成缩略图路径
 * @param type $path
 * @param type $prefix
 * @param type $sufix
 * @return type
 * @since 1.0 <2015-11-02> SoChishun Added.
 */
function sofn_rename_file($path, $prefix = 's_', $sufix = '') {
    $pathinfo = pathinfo($path);
    return $pathinfo['dirname'] . '/' . $prefix . $pathinfo['filename'] . $sufix . '.' . $pathinfo['extension'];
}

/**
 * 返回指定路径下的内容
 * @param string $directory 相对路径
 * @param array $config 选项
 * @return array
 * @throws Exception
 * @since 1.0 <2015-5-11> SoChishun Added.
 * @since 1.1 <2015-10-8> SoChishun 新增filetype文件类别属性
 */
function sofn_read_dir($directory, $options = array()) {
    $config = array('name' => true, 'path' => true, 'real_path' => true, 'relative_path' => false, 'exten' => false, 'ctime' => false, 'mtime' => false, 'size' => false, 'is_dir' => true, 'is_file' => false, 'is_link' => false, 'is_executable' => false, 'is_readable' => false, 'is_writable' => false, 'filetype' => false);
    if ($options) {
        $config = array_merge($config, $options);
    }
    try {
        $dir = new DirectoryIterator(sofn_path_rtoa($directory));
    } catch (Exception $e) {
        throw new Exception($directory . ' is not readable');
    }
    // 文件分类
    $filetypes = array('zip' => array('zip', 'rar', '7-zip', 'tar', 'gz', 'gzip'), 'doc' => array('txt', 'rtf', 'doc', 'docx', 'ppt', 'pptx', 'xls', 'xlsx', 'wps', 'et'), 'script' => array('php', 'js', 'css', 'c'), 'image' => array('jpg', 'jpeg', 'png', 'gif', 'tiff', 'psd', 'bmp', 'ico'));
    $files = array();
    foreach ($dir as $file) {
        if ($file->isDot()) {
            continue;
        }
        if ($config['name']) {
            $item['name'] = $file->getFileName();
        }
        if ($config['path']) {
            $item['path'] = $file->getPath();
        }
        if ($config['real_path']) {
            $item['real_path'] = $file->getRealPath();
        }
        if ($config['relative_path']) {
            $item['relative_path'] = sofn_path_ator($file->getRealPath());
        }
        $exten = $file->getExtension();
        if ($config['exten']) {
            $item['exten'] = $exten;
        }
        if ($config['filetype']) {
            $scategory = '';
            foreach ($filetypes as $catetory => $extens) {
                if (in_array($exten, $extens)) {
                    $scategory = $catetory;
                    break;
                }
            }
            $item['filetype'] = $scategory;
        }
        if ($config['mtime']) {
            $item['mtime'] = $file->getMTime();
        }
        if ($config['ctime']) {
            $item['ctime'] = $file->getCTime();
        }
        if ($config['size']) {
            $item['size'] = $file->getSize();
        }
        if ($config['is_dir']) {
            $item['is_dir'] = $file->isDir();
        }
        if ($config['is_file']) {
            $item['is_file'] = $file->isFile();
        }
        if ($config['is_link']) {
            $item['is_link'] = $file->isLink();
        }
        if ($config['is_executable']) {
            $item['is_executable'] = $file->isExecutable();
        }
        if ($config['is_readable']) {
            $item['is_readable'] = $file->isReadable();
        }
        if ($config['is_writable']) {
            $item['is_writable'] = $file->isWritable();
        }
        $files[] = $item;
    }
    return $files;
}

/**
 * 将秒钟数格式化为时分秒的时间
 * @param int $seconds 秒钟数
 * @return string 时分秒时间格式
 * @since 1.0 2014-9-12 by sutroon
 * @example sofn_seconds_format(61) // 00:01:01
 */
function sofn_seconds_format($seconds) {
    if (!$seconds) {
        return '00:00:00';
    }
    if (!is_numeric($seconds)) {
        return $seconds;
    }
    $h = intval($seconds / 3600);
    $m = intval(($seconds % 3600) / 60);
    $s = $seconds % 60;

    return substr('0' . $h, -2) . ':' . substr('0' . $m, -2) . ':' . substr('0' . $s, -2);
}

/**
 * 时间轴开发，即显示为“刚刚”、“5分钟前”、“昨天10:23”等
 * @param string $time
 * @return string
 * @since 1.0.0 2013-5-4 sutroon
 * @example sofn_tran_time('2013-5-4 13:23:33');
 */
function sofn_tran_time($time) {
    if (!is_numeric($time)) {
        $time = strtotime($time);
    }
    $rtime = date("m-d H:i", $time);
    $htime = date("H:i", $time);
    $time = time() - $time;
    if ($time < 60) {
        $str = '刚刚';
    } elseif ($time < 60 * 60) {
        $min = floor($time / 60);
        $str = $min . '分钟前';
    } elseif ($time < 60 * 60 * 24) {
        $h = floor($time / (60 * 60));
        $str = $h . '小时前 ' . $htime;
    } elseif ($time < 60 * 60 * 24 * 3) {
        $d = floor($time / (60 * 60 * 24));
        if ($d == 1) {
            $str = '昨天 ' . $rtime;
        } else {
            $str = '前天 ' . $rtime;
        }
    } else {
        $str = $rtime;
    }
    return $str;
}

/**
 * 计算时间差
 * @param string $time1 时间1
 * @param string $time2 时间2
 * @param string $format 格式
 * @return string
 * @since 1.0 <2015-4-24> SoChishun Added.
 * @example sofn_date_diff('2015-4-24 10:20:30','2015-4-24 17:42:33','h:i');
 */
function sofn_date_diff($time1, $time2, $format = 'd天h时i分') {
    $diff = strtotime($time2) - strtotime($time1);
    $date['d'] = floor($diff / 86400);
    $timepart = $diff % 86400;
    $date['h'] = floor($timepart / 3600);
    $date['H'] = $date['h'] < 10 ? '0' . $date['h'] : $date['h'];
    $date['i'] = floor($timepart % 3600 / 60);
    $date['I'] = $date['i'] < 10 ? '0' . $date['i'] : $date['i'];
    $date['s'] = floor($timepart % 60);
    $date['S'] = $date['s'] < 10 ? '0' . $date['s'] : $date['s'];
    foreach ($date as $key => $value) {
        $format = str_replace($key, $value, $format);
    }
    return $format;
}

/**
 * 强制弹出保存文件对话框
 * @param string $default_file_name 对话框中默认文件名
 * @param string $content 要保存的内容
 * @param bool $is_path 指示$content参数是内容还是文件路径,默认false
 * @since 1.0 2014-10-11 by sutroon; 1.1 2015-1-5 by sutroon 新增路径支持功能
 * @example sofn_save_file_dialog('image1.jpg',file_get_contents($path));
 */
function sofn_save_file_dialog($default_file_name, $content, $is_path = false) {
    if ($is_path) {
        if (file_exists($content)) {
//打开文件  
            $file = fopen($content, "r");
//输入文件标签   
            Header("Content-type: application/octet-stream");
            Header("Accept-Ranges: bytes");
            Header("Accept-Length: " . filesize($content));
            Header("Content-Disposition: attachment; filename=" . $default_file_name);
//读取文件内容并直接输出到浏览器  
            echo fread($file, filesize($content));
            fclose($file);
            exit();
        } else {
            die('抱歉, 服务器上文件已删除或更名, 请联系管理员!');
        }
    } else {
        if (false === $content) {
            die('抱歉, 服务器上文件已删除或更名, 请联系管理员!');
        }
        header("Content-Type: application/force-download");
        header('Content-Disposition: attachment; filename="' . $default_file_name . '"');
        echo $content;
        exit(0);
    }
}

/**
 * 导出Excel
 * @param array $list 数据集
 * @param array $options 更多选项,包含 string:FILENAME, string:SHEETNAME, int:ActiveSheet
 * @since 1.0 2014-9-5 by sutroon
 * @since 1.1 <2015-4-10> SoChishun 修正导出大数值自动变为科学计数的问题
 * @since 1.2 2016-7-18 SoChishun 修复在部分服务器中导出的XLS提示不能识别的文件格式的问题
 * @example sofn_excel_export($list);
 */
function sofn_excel_export($list, $options = array()) {
    $options = array_merge(array('FileName' => 'Excel' . date('Ymd-His'), 'SheetName' => 'Sheet1', 'ActiveSheet' => 0, 'SaveTemp' => false, 'Creator' => 'XCall'), is_array($options) ? $options : array());
    vendor('PHPExcel.PHPExcel');
    $objPHPExcel = new \PHPExcel();
    $objPHPExcel->getProperties()->setCreator($options['Creator'])
            ->setLastModifiedBy("PHPExcel")
            ->setTitle("Office 2007 XLSX Test Document")
            ->setSubject("Office 2007 XLSX Test Document")
            ->setDescription("Test document for Office 2007 XLSX, generated using PHP classes.")
            ->setKeywords("office 2007 openxml php")
            ->setCategory("Test result file");
    $sheet = $objPHPExcel->getActiveSheet($options['ActiveSheet']);
    $i = 1;
    foreach ($list as $row) {
        if ($i == 1) {
            $c = 0;
            foreach ($row as $k => $v) {
                $sheet->setCellValueByColumnAndRow($c, $i, $k);
                $c++;
            }
        }
        $i++;
        $c = 0;
        foreach ($row as $k => $v) {
            //$sheet->setCellValueByColumnAndRow($c, $i, $v);
            $sheet->setCellValueExplicitByColumnAndRow($c, $i, $v, \PHPExcel_Cell_DataType::TYPE_STRING);
            $c++;
        }
    }
    $objPHPExcel->getActiveSheet()->getHeaderFooter()->setOddHeader('&L&G&C&HPlease treat this document as confidential!');
    $objPHPExcel->getActiveSheet()->getHeaderFooter()->setOddFooter('&L&B' . $objPHPExcel->getProperties()->getTitle() . '&RPage &P of &N');
    $objPHPExcel->getActiveSheet()->setTitle($options['SheetName']);
    $filename = $options['FileName'] . '.xlsx';
    //$filename=  iconv('UTF-8', 'ISO_8859_1', $filename);
    ob_end_clean(); // 导出的XLS提示不能识别的文件格式(see http://www.dreamgoo.com/archives791.html)
    //header('Content-Type: application/vnd.ms-excel');
    header('Content-Type: application/octet-stream');
    header("Content-Disposition: attachment;filename=$filename");
    header('Cache-Control: max-age=0');
    $objWriter = \PHPExcel_IOFactory::createWriter($objPHPExcel, 'Excel2007');
    $objWriter->save('php://output');
    exit(0);
}

/**
 * 导出文件
 * @param string $filename
 * @since 1.0 <2015-9-21> SoChishun Added.
 */
function sofn_file_export($filename) {
    header('Content-Type: application/octet-stream');
    header("Content-Disposition: attachment;filename=$filename");
    header('Cache-Control: max-age=0');
}

/**
 * 将Excel文件转为数组
 * @param string $path Excel文件的服务器路径
 * @param array $options 更多选项,包含 int:START, int:COUNT, int:ActiveSheet
 * @return mixed 如果文件存在则返回array,否则返回false,数组键是标题名称
 * @since 1.0 2014-9-5 by sutroon
 * @since 1.1 <2015-4-29> SoChishun 新增ONLY_GET_COUNT参数用于统计
 * @example $arr_content = sofn_excel_import(UPLOAD_ROOT . $msg['filePath']);
 */
function sofn_excel_import($path, $options = array()) {
    $options = array_merge(array(
        'START' => 2, // 起始数据行,默认2(第一行为标题)
        'COUNT' => 0, // 数量,0=不限
        'ActiveSheet' => 0, // 页面索引,默认0
        'ONLY_GET_COUNT' => false, // 是否只返回数量而不是数据数组(用于数量判断)
            ), is_array($options) ? $options : array());
    if (!file_exists($path)) {
        return false;
    }
    vendor('PHPExcel.PHPExcel');
    $objPHPExcel = \PHPExcel_IOFactory::load($path);
    $sheet = $objPHPExcel->getActiveSheet($options['ActiveSheet']);
    $highestRow = $sheet->getHighestRow(); // 取得总行数,数值,如:30
    $highestColumnName = $sheet->getHighestColumn(); // 取得总列数,字符,如:F
    $highestColumn = \PHPExcel_Cell::columnIndexFromString($highestColumnName); // 转换成总列数数值
    if ($options['ONLY_GET_COUNT']) { // 如果只是返回数量统计,则到此退出,提高效率
        return array('rows' => $highestRow, 'cols' => $highestColumn);
    }
    $aout = array();
    $i = $options['START'];
    $count = $options['COUNT'];
    while ($i <= $highestRow) {
        if ($count > 0 && ($i - 2) > $count) {
            break;
        }
        for ($c = 0; $c < $highestColumn; $c++) {
            $title = $sheet->getCellByColumnAndRow($c, 1)->getValue(); // 格式化标题行
            if ($title instanceof \PHPExcel_RichText) { //富文本转换字符串
                $title = $title->__toString();
            }
            $cell = $sheet->getCellByColumnAndRow($c, $i);
            $value = $cell->getValue();
            if ($value instanceof \PHPExcel_RichText) { //富文本转换字符串
                $value = $value->__toString();
            }
            if ($cell->getDataType() == \PHPExcel_Cell_DataType::TYPE_NUMERIC) { // 格式化日期
                $cellstyleformat = $cell->getStyle()->getNumberFormat();
                $formatcode = $cellstyleformat->getFormatCode();
                if (preg_match('/^(\[\$[A-Z]*-[0-9A-F]*\])*[hmsdy]/i', $formatcode)) {
                    $value = gmdate("Y-m-d", \PHPExcel_Shared_Date::ExcelToPHP($value));
                } else {
                    $value = \PHPExcel_Style_NumberFormat::toFormattedString($value, $formatcode);
                }
            }
            $aout[$i - 2][$title] = $value;
        }
        $i++;
    }
    return $aout;
}

/**
 * 生成select的option元素
 * <p>
 * sofn_combobox_option(array('EQ'=>'等于','GT'=>'大于','LT'=>'小于'),$search['operate_date_start'],'EQ')
 * </p>
 * @param array $aitems
 * @param mixed $value
 * @param mixed $default
 * @return string
 * @since 1.0 2016-5-27
 */
function sofn_combobox_option($aitems, $value, $default) {
    $outstr = '';
    if (!$value) {
        $value = $default;
    }
    foreach ($aitems as $id => $text) {
        $outstr.='<option value="' . $id . '"' . ($value && $value == $id ? ' selected="selected"' : '') . '>' . $text . '</option>';
    }
    return $outstr;
}


/**
 * 获取服务端IP地址
 * @return string
 * @since 1.0 2016-7-1 SoChishun Added.
 */
function get_host_ip() {
    return isset($_SERVER['HTTP_X_FORWARDED_HOST']) ? $_SERVER['HTTP_X_FORWARDED_HOST'] : (isset($_SERVER['HTTP_HOST']) ? $_SERVER['HTTP_HOST'] : '');
}